package com.masonluo.mlonlinejudge.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.masonluo.mlonlinejudge.model.bo.IoSampleBo;
import com.masonluo.mlonlinejudge.model.bo.TagBo;
import com.masonluo.mlonlinejudge.model.dto.ProblemUploadDto;
import com.masonluo.mlonlinejudge.model.dto.TestCaseUploadDto;
import com.masonluo.mlonlinejudge.model.param.ProblemParam;
import com.masonluo.mlonlinejudge.service.ImportProblemService;
import com.masonluo.mlonlinejudge.service.ProblemService;
import com.masonluo.mlonlinejudge.service.ProgramAnswerService;
import com.masonluo.mlonlinejudge.service.TestcaseFileService;
import com.masonluo.mlonlinejudge.utils.DirectoryUtils;
import com.masonluo.mlonlinejudge.utils.ZipUtils;
import net.lingala.zip4j.ZipFile;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;


@Service
/**
 * @author mingkai
 */
public class ImportProblemServiceImpl implements ImportProblemService {

    private static final Logger log = LoggerFactory.getLogger(ImportProblemServiceImpl.class);

    @Autowired
    private TestcaseFileService testcaseFileService;

    @Autowired
    private ProblemService problemService;

    @Autowired
    private ProgramAnswerService programAnswerService;

    //上传的文件临时存储路径
    private final String tempFileDir = "D:\\mloj\\temp";
    //上传的文件解压的路径
    private final String desDir = "D:\\mloj\\des";
    //每道题目构建的测试样例压缩文件临时存储路径
    private final String testcaseZipDir = "D:\\mloj\\testcase.zip";

    /**
     * 将上传的文件读取每道编程题的题目信息，并读取测试样例，然后关联
     *
     * @param file
     * @throws IOException
     */
    @Override
    public void importProblem(MultipartFile file) throws IOException {
        unzipFile(file);
        buildProblemAndTestcase();
    }


    /**
     * 将上传的文件解压到des下:
     * 第一级文件夹：UUID为名，其下有n个二级文件夹
     * 第二级文件夹：数字为名，其下有1个problem.json文件和1个testcase文件夹
     * problem.json文件：题目信息的json格式描述
     * testcase文件夹：其下有n个文件: in文件 out文件
     *
     * @param file 上传的压缩文件
     * @throws IOException
     */
    public void unzipFile(MultipartFile file) throws IOException {
        File temp = new File(tempFileDir);
        if (!temp.exists()) {
            temp.mkdirs();
        }
        file.transferTo(temp);
        ZipFile zipFile = new ZipFile(temp);
        //去掉外层的压缩文件夹
        zipFile.extractAll(desDir);
        //desDir目录对象
        File desDirFile = new File(desDir);
        //查看内层是否为压缩文件夹：如果是，再去掉内层的压缩文件夹，再将若干个二级文件夹放入UUID文件夹
        //否则，直接将内层的所有二级文件夹放入UUID文件夹
        File[] files = desDirFile.listFiles();
        String curFolderName = UUID.randomUUID().toString();
        for (File cur : files) {
            ZipFile curZipFile = new ZipFile(cur);
            //当前文件全部为压缩文件
            if (curZipFile.isValidZipFile()) {
                //放置当前压缩文件夹的内容到一个以UUID命名的新文件夹
                curFolderName = UUID.randomUUID().toString();
                curZipFile.extractAll(desDir + "\\" + curFolderName);
                //删除当前压缩文件
                cur.delete();
            }
            //将des下的所有二级文件夹移入UUID文件夹
            else
            {

                try {
                    FileUtils.moveDirectory(cur,new File(desDir+"\\"+curFolderName+"\\"+cur.getName()));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 根据des目录下的文件构造题目信息和测试样例文件
     */
    public void buildProblemAndTestcase(){
        File desFile = new File(desDir);
        //UUID文件夹
        File[] folderOfUUID = desFile.listFiles();
        try {
            for (File folder1 : folderOfUUID) {
                if (folder1.isDirectory()) {
                    //folderOfProblem下:testcase文件夹和problem.json
                    File[] folderOfProblem = folder1.listFiles();
                    for (File folder2 : folderOfProblem) {
                        if (folder2.isDirectory()) {
                            File[] metaProblem = folder2.listFiles();
                            String testcaseId = "";
                            //问题上传
                            ProblemUploadDto dto = null;
                            //问题上传返回带有题目id的对象
                            ProblemParam uploadResult = null;
                            //测试样例上传
                            TestCaseUploadDto testCaseUploadDto = null;
                            for (File file3 : metaProblem) {
                                if (file3.isDirectory()) {
                                    //测试样例的in.out文件数组
                                    File[] testcase = file3.listFiles();
                                    //压缩测试样例文件到testcase.zip
                                    ZipUtils.zipMultipleFiles(testcaseZipDir, testcase);
                                    //将ZipFile类型文件转换成MultipartFile类型
                                    //测试样例压缩包
                                    File testcaseZip = new File(testcaseZipDir);
                                    FileItem fileItem = new DiskFileItem("file", Files.probeContentType(testcaseZip.toPath()),
                                            false, testcaseZip.getName(), (int) testcaseZip.length(), testcaseZip.getParentFile());
                                    IOUtils.copy(new FileInputStream(testcaseZip), fileItem.getOutputStream());
                                    MultipartFile multipartFile = new CommonsMultipartFile(fileItem);
                                    //上传测试样例文件
                                    testCaseUploadDto = testcaseFileService.sendMultipartFile(multipartFile);
                                    testcaseId = testCaseUploadDto.getTestCaseId();
                                } else {//该文件是json文件
                                    String JsonStr = readJsonFile(file3);
                                    dto = convertJSONStrToProblem(JsonStr);
                                }
                            }
                            if (testcaseId != null && testcaseId != "") {
                                //将问题和测试样例id关联
                                dto.setTestCaseId(testcaseId);
                                //上传编程题信息后返回包含题目id的对象
                                uploadResult =  problemService.uploadProblem(dto);
                                //将测试样例文件的输入输出和测试样例id存储进入t_program_answer
                                int row = programAnswerService.insertAnswer(testCaseUploadDto.getTestCaseId(),testCaseUploadDto.getAnswers(),testCaseUploadDto.getInputContents());
                                //将问题id更新进入上述t_program_answer与testcaseId关联起来
                                programAnswerService.updateAnswer(testCaseUploadDto.getTestCaseId(),uploadResult.getId());
                                log.info("");
                            } else {
                                log.error("测试样例上传失败");
                            }
                        }
                    }
                }
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
        finally {
            //删除文件夹及其下文件
            DirectoryUtils.deleteDir(desFile);
        }
    }




    /**
     * 将一个JSON字符串转换成ProblemUploadDto
     *
     * @param JSONStr 待转换的JSON字符串
     * @return dto 封装好的ProblemUploadDto
     */
    public ProblemUploadDto convertJSONStrToProblem(String JSONStr) {
        //文件的JSON对象
        JSONObject obj = JSON.parseObject(JSONStr);
        //题目标题
        String title = (String) obj.get("title");
        //题目整体描述
        JSONObject descJSON = (JSONObject) obj.get("description");
        //题目文本描述
        String description = (String) descJSON.get("value");
        //输入整体描述
        JSONObject inputJSON = (JSONObject) obj.get("input_description");
        //输入文本描述
        String inputDescription = (String) inputJSON.get("value");
        //输出整体描述
        JSONObject outputJSON = (JSONObject) obj.get("output_description");
        //输出文本描述
        String outputDescription = (String) outputJSON.get("value");
        //时间限制(ms)
        Integer timeLimit = (Integer) obj.get("time_limit");
        //内存限制(MB)
        Integer memoryLimit = (Integer) obj.get("memory_limit");
        //级别
        String level = "easy";
        //提示的JSON对象
        JSONObject hintJSON = (JSONObject) obj.get("hint");
        //提示文本
        String hint = (String) hintJSON.get("value");

        //标签数组
        List<TagBo> tags = new ArrayList<>();
        JSONArray tagsArray = obj.getJSONArray("tags");
        //存入List<TagBo>
        for (Object tagObj : tagsArray) {
            TagBo tagBo = new TagBo();
            tagBo.setName((String) tagObj);
            tags.add(tagBo);
        }

        //输入输出样例数组
        List<IoSampleBo> ioSamples = new ArrayList<>();
        JSONArray ioSamplesJSONArray = (JSONArray) obj.get("samples");
        for (int i = 0; i < ioSamplesJSONArray.size(); i++) {
            JSONObject ioSample = (JSONObject) ioSamplesJSONArray.get(i);
            //封装单个IoSampleBo对象
            IoSampleBo ioSampleBo = new IoSampleBo();
            String input = (String) ioSample.get("input");
            String output = (String) ioSample.get("output");
            ioSampleBo.setInputSample(input);
            ioSampleBo.setOutputSample(output);
            //存入集合
            ioSamples.add(ioSampleBo);
        }
        //放入ProblemUploadDto对象
        ProblemUploadDto dto = new ProblemUploadDto();
        dto.setUserId(1);
        dto.setTitle(title);
        dto.setDescription(description);
        dto.setInputDescription(inputDescription);
        dto.setOutputDescription(outputDescription);
        dto.setTimeLimit(timeLimit);
        dto.setMemoryLimit(memoryLimit);
        dto.setLevel(0);
        dto.setHint(hint);
        dto.setVisible(true);
        dto.setTags(tags);
        dto.setIoSamples(ioSamples);
        return dto;
    }

    /**
     * 读取json文件，返回json串
     *
     * @param jsonFile 待读取的JSON文件
     * @return JSON字符串
     */
    public static String readJsonFile(File jsonFile) {
        String jsonStr = "";
        try {
            FileReader fileReader = new FileReader(jsonFile);

            Reader reader = new InputStreamReader(new FileInputStream(jsonFile), "utf-8");
            int ch = 0;
            StringBuffer sb = new StringBuffer();
            while ((ch = reader.read()) != -1) {
                sb.append((char) ch);
            }
            fileReader.close();
            reader.close();
            jsonStr = sb.toString();
            return jsonStr;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void main(String[] args) {
        // String JSONStr = readJsonFile("problem.json");
        // ProblemUploadDto dto = convertJSONStrToProblem(JSONStr);
        //System.out.println(dto);
    }
}
